/*
 * DAO.java
 * 
 * The data access object for database.
 * 
 * Copyright 2013 Santi Ruiz Andrés <starfly1570@gmail.com>
 * 
 * This file is part of Drawer of Marks.
 * 
 * Drawer of Marks is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Drawer of Marks is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Drawer of Marks.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package blogspot.santiruizpro.drawerofmarks.model;

import java.io.File;
import java.util.ArrayList;

import android.content.ContentValues;
import android.content.Context;
import android.os.Environment;

/**
 * @author starfly1570
 */
public class DAO {
	
	public static final String EXPORT_FILE = "drawer_marks.mp";
	private ArrayList<Subject> subjects;
	
	private Importer importer;
	private Exporter exporter;
	private DBAccess dataBase;
	
	public DAO(Context context) {
		dataBase = new DBAccess(context);
		importer = new Importer();
		exporter = new Exporter();
		buildObjects();
	}
	
	/**
	 * Builds all objects from database.
	 */
	private void buildObjects() {
		subjects = buildSubjects();
	}
	
	/**
	 * Stores a subject into database.
	 * 
	 * @param subjectName the name of subject
	 * @param isWeighted true if it is a weighted subject, false otherwise
	 */
	public void addSubject(String subjectName, boolean isWeighted) {
		ContentValues cv = new ContentValues();
		cv.put("name", subjectName);
		int iw = isWeighted ? 1 : 0;
		cv.put("isweighted", iw);
		cv.put("is_active", 1);
		dataBase.insert("subjects",cv);
		buildObjects();
	}
	
	/**
	 * Stores a division into database.
	 * 
	 * @param name the name of division
	 * @param isWeighted true if it is a weighted division, false otherwise
	 * @param weight % of weighting related to containing subject
	 * @param subject the id of containing subject
	 */
	public void addDivision(String name, boolean isWeighted, float weight, int subject) {
		ContentValues cv = new ContentValues();
		cv.put("name", name);
		int iw = isWeighted ? 1 : 0;
		cv.put("isweighted", iw);
		cv.put("weight",weight);
		cv.put("subjectkey", subject);
		cv.put("is_active", 1);
		dataBase.insert("divisions",cv);
		buildObjects();
	}
	
	/**
	 * Stores an exam into database.
	 * 
	 * @param name the name of exam
	 * @param mark the mark of the exam
	 * @param weight % of weighting related to containing division
	 * @param division the id of containing division
	 */
	public void addExam(String name, float mark, float weight, int division) {
		ContentValues cv = new ContentValues();
		cv.put("name", name);
		cv.put("mark", mark);
		cv.put("weight", weight);
		cv.put("divisionkey", division);
		cv.put("is_active", 1);
		dataBase.insert("exams",cv);
		buildObjects();
	}
	
	/**
	 * Removes an exam from database.
	 * 
	 * @param id the exam id
	 */
	public void removeExam(int id) {
		dataBase.delete("exams", "pkey = ?", new String[] { id + "" });
		buildObjects();
	}
	
	/**
	 * Removes a division from database.
	 * 
	 * @param id the division id
	 */
	public void removeDivision(int id) {
		dataBase.delete("divisions", "pkey = ?", new String[] { id + "" });
		buildObjects();
	}
	
	/**
	 * Remove a subject from database.
	 * 
	 * @param id the subject id
	 */
	public void removeSubject(int id) {
		dataBase.delete("subjects", "pkey = ?", new String[] { id + "" });
		buildObjects();
	}
	
	/**
	 * Removes all data from database.
	 */
	public void resetDataBase() {
		dataBase.delete("subjects", null, null);
		dataBase.delete("divisions", null, null);
		dataBase.delete("exams", null, null);
		buildObjects();
	}
	
	// Getters
	
	public ArrayList<Subject> getSubjects() {
		return this.subjects;
	}
	
	public Subject getSubject(int id) {
		for (Subject s : subjects) {
			if (s.getId() == id)
				return s;
		}
		
		return null;
	}
	
	public Division getDivision(int id) {
		for (Subject s : subjects) {
			for (Division d : s.getDivisions()) {
				if ( d.getId() == id )
					return d;
			}
		}
		
		return null;
	}
	
	/**
	 * Builds all subjects from database.
	 * 
	 * @return a list of subjects
	 */
	private ArrayList<Subject> buildSubjects() {
		ArrayList<ArrayList<String>> query = dataBase.query("Select * from subjects");
		ArrayList<Subject> result = new ArrayList<Subject>();
		for ( int i = 0; i < query.size(); i++ ) {
			ArrayList<String> row = query.get(i);
			int id = Integer.parseInt(row.get(0));
			String name = row.get(1);
			boolean isWeighted = Integer.parseInt(row.get(2)) == 1 ? true : false;
			int isActive = row.get(3) == null ? 0 : Integer.parseInt(row.get(3));
			
			ArrayList<Division> divisions = buildSubjectDivisions(id);
			Subject subject = new Subject(id,name,divisions,isWeighted,isActive == 1);
			result.add(subject);
		}
		
		return result;
	}
	
	/**
	 * Builds all divisions of a subject from database.
	 * 
	 * @param sID the subject id
	 * @return a list of divisions
	 */
	private ArrayList<Division> buildSubjectDivisions(int sID) {
		ArrayList<ArrayList<String>> query = dataBase.query("Select * from divisions");
		ArrayList<Division> result = new ArrayList<Division>();
		for ( int i = 0; i < query.size(); i++ ) {
			ArrayList<String> row = query.get(i);
			int fkey = Integer.parseInt(row.get(4));
			if ( fkey == sID) {
				int id = Integer.parseInt(row.get(0));
				String name = row.get(1);
				boolean isWeighted = Integer.parseInt(row.get(2)) == 1 ? true : false;
				float weight = Float.parseFloat(row.get(3));
				int isActive = row.get(5) == null ? 0 : Integer.parseInt(row.get(5));
				
				ArrayList<Exam> exams = buildDivisionExams(id);
				result.add(new Division(id,name,exams,weight,isWeighted,isActive == 1));
			}
		}
		
		return result;
	}
	
	/**
	 * Builds all exams of a division from database.
	 * 
	 * @param dID the division id
	 * @return a list of exams
	 */
	private ArrayList<Exam> buildDivisionExams(int dID) {
		ArrayList<ArrayList<String>> query = dataBase.query("Select * from exams");
		ArrayList<Exam> result = new ArrayList<Exam>();
		for ( int i = 0; i < query.size(); i++ ) {
			ArrayList<String> row = query.get(i);
			int fkey = Integer.parseInt(row.get(4));
			if ( fkey == dID ) {
				int id = Integer.parseInt(row.get(0));
				String name = row.get(1);
				float mark = Float.parseFloat(row.get(2));
				float weight = Float.parseFloat(row.get(3));
				int isActive = row.get(5) == null ? 0 : Integer.parseInt(row.get(5));
				
				result.add(new Exam(id,name,mark,weight,isActive == 1));
			}
		}
		
		return result;
	}
	
	/**
	 * Updates the activation field of an exam.
	 * 
	 * @param examID the id of exam
	 * @param active activation state
	 */
	public void updateExamActivation(int examID, boolean active) {
		int update = active ? 1 : 0;
		dataBase.update("update exams set is_active=" + update + " where pkey = " + examID);
		buildObjects();
	}
	
	/**
	 * Updates the activation field of a division.
	 * 
	 * @param examID the id of division
	 * @param active activation state
	 */
	public void updateDivisionActivation(int divisionID, boolean active) {
		int update = active ? 1 : 0;
		dataBase.update("update divisions set is_active=" + update + " where pkey = " + divisionID);
		buildObjects();
	}
	
	/**
	 * Updates the activation field of a subject.
	 * 
	 * @param examID the id of subject
	 * @param active activation state
	 */
	public void updateSubjectActivation(int subjectID, boolean active) {
		int update = active ? 1 : 0;
		dataBase.update("update subjects set is_active=" + update + " where pkey = " + subjectID);
		buildObjects();
	}
	
	/**
	 * Asks the device if the SD Card is available or not.
	 * 
	 * @return true if SD Card is available, false otherwise
	 */
	public boolean isSDAvailable() {
		return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
	}
	
	/**
	 * Returns the default export file in SD Card.
	 * 
	 * @return a file
	 */
	public File getExportFile() {
		String directory = Environment.getExternalStorageDirectory().getAbsolutePath();
		return new File(directory + "/" + EXPORT_FILE);
	}
	
	/**
	 * Exports database into a file in SD Card.
	 */
	public void exportDataBase() {
		exporter.exportDataBase(dataBase, getExportFile());
	}
	
	/**
	 * Imports all info from default export file in SD Card.
	 */
	public void importMarksPackage() {
		importer.importMarksPackage(dataBase,getExportFile());
		buildObjects();
	}
	
}
